package com.sunrise.xdoc.view.config;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;

import org.apache.commons.collections.CollectionUtils;
import org.primefaces.context.RequestContext;

import com.sunrise.xdoc.dto.KhuVucDTO;
import com.sunrise.xdoc.entity.config.Ban;
import com.sunrise.xdoc.entity.order.DonHang;
import com.sunrise.xdoc.entity.order.DonHang.TrangThaiDonHang;
import com.sunrise.xdoc.event.EventData;
import com.sunrise.xdoc.event.ListenerMethod;
import com.sunrise.xdoc.service.IConfigService;
import com.sunrise.xdoc.service.IFinanceService;
import com.sunrise.xdoc.service.IOrderService;
import com.sunrise.xdoc.utils.SunriseStringUtils;
import com.sunrise.xdoc.view.catalog.BanHang;

/**
 * Danh sách bàn theo từng khu vực.
 * 
 * @author phphuc
 * 
 */
@ManagedBean
@SessionScoped
public class DanhSachBanKhuVuc implements Serializable {
	private static final long serialVersionUID = -974539902070354940L;

	@ManagedProperty(value = "#{configService}")
	private IConfigService configService;

	@ManagedProperty(value = "#{orderService}")
	private IOrderService orderService;

	@ManagedProperty(value = "#{financeService}")
	private IFinanceService financeService;

	@ManagedProperty(value = "#{banHang}")
	private BanHang banHang;

	private List<KhuVucDTO> kvDTOs;

	private Map<Ban, DonHang> ban2DonHang = new HashMap<>();
	private Map<DonHang, Set<Ban>> donHang2Bans = new HashMap<>();

	public Map<Ban, DonHang> getBan2DonHang() {
		return ban2DonHang;
	}

	public Map<DonHang, Set<Ban>> getDonHang2Bans() {
		return donHang2Bans;
	}

	public void setBan2DonHang(Map<Ban, DonHang> ban2DonHang) {
		this.ban2DonHang = ban2DonHang;
	}

	public void setDonHang2Bans(Map<DonHang, Set<Ban>> donHang2Bans) {
		this.donHang2Bans = donHang2Bans;
	}

	public void setSelectedBans(Set<Ban> selectedBans) {
		this.selectedBans = selectedBans;
	}

	private Set<Ban> selectedBans = new HashSet<>(1);

	@PostConstruct
	public void init() {
		kvDTOs = configService.getKhuVucDTOs();

		List<DonHang> donHangs = orderService.getDonHangChuaThanhToans();
		ban2DonHang.clear();
		donHang2Bans.clear();
		for (DonHang dh : donHangs) {
			donHang2Bans.put(dh, dh.getBans());
			for (Ban b : dh.getBans()) {
				ban2DonHang.put(b, dh);
			}
		}
	}

	@ListenerMethod(acceptedEvents = { "saveOrUpdateBan", "deleteBan", "saveOrUpdateKhuVuc", "deleteKhuVuc" })
	public void processEvent(EventData eventData, String eventName) {
		// reinit data when Ban or KhuVuc updated, deleted
		// TODO this can be optimized. Just update the data, do not need to
		// recall the service.
		init();
	}

	public DonHang getDonHang(Ban ban) {
		return ban2DonHang.get(ban);
	}

	/**
	 * Mở bàn mới, chuyển sang màn hình bán hàng.
	 * 
	 * @param ban
	 *            bàn được mở
	 * @return đơn hàng mới
	 */
	public String moBan() {
		DonHang donHang = new DonHang();
		donHang.setCode(String.valueOf(orderService.getGeneratedCode()));
		donHang.setThoiGianDatHang(new Date());
		donHang.setMaNhanVienPhucVu("HPP"); // TODO
		donHang.setTrangThai(TrangThaiDonHang.DAT_HANG);
		donHang.getBans().addAll(selectedBans);
		orderService.saveOrUpdateDonHang(donHang);
		for (Ban b : selectedBans) {
			ban2DonHang.put(b, donHang);
		}
		donHang2Bans.put(donHang, donHang.getBans());
		banHang.setDonHang(donHang);
		return "/pages/catalog/banHang";
	}

	public boolean isSelectedBan(Ban ban) {
		return selectedBans.contains(ban);
	}

	/**
	 * Get các bàn có cùng hóa đơn
	 * 
	 * @param ban
	 * @return
	 */
	public Set<Ban> getGroupBans(Ban ban) {
		DonHang dh = ban2DonHang.get(ban);
		Set<Ban> bans = donHang2Bans.get(dh);
		if (CollectionUtils.isEmpty(bans)) {
			bans = new HashSet<>();
			bans.add(ban);
		}
		return bans;
	}

	/**
	 * Chuyển các movedToBans thành selectedBans. Đồng thời chuyển đơn
	 * hàng đang được chọn sang movedToBans.<br/>
	 * Condition: phải có bàn được chọn trước đó.
	 * 
	 * @param movedToBans
	 */
	public void chuyenBan(Set<Ban> movedToBans) {
		// //////// Gộp đơn hàng
		// DonHang selectedDH = ban2DonHang.get(selectedBans.iterator().next());
		// Set<DonHang> donHangs = findDonHangs(movedToBans);
		// donHangs.add(selectedDH);
		// DonHang newDonHang = orderService.gopDonHangs(donHangs, movedToBans);
		//
		// // Clear map data
		// for (Ban ban : selectedBans) {
		// ban2DonHang.remove(ban);
		// }
		// for (Ban ban : movedToBans) {
		// ban2DonHang.remove(ban);
		// }
		// for (DonHang dh : donHangs) {
		// donHang2Bans.remove(dh);
		// }
		//
		// // Gộp bàn
		// selectedBans.clear();
		// selectedBans.addAll(movedToBans);
		// for (Ban ban : selectedBans) {
		// ban2DonHang.put(ban, newDonHang);
		// }
		// donHang2Bans.put(newDonHang, new HashSet<>(selectedBans));
		//
		// banHang.setDonHang(newDonHang);
	}

	public String getSelectedBansLabel() {
		if (selectedBans.isEmpty()) {
			return "";
		}
		StringBuffer label = new StringBuffer();
		for (Ban b : selectedBans) {
			label.append(b.getName()).append(", ");
		}
		return label.substring(0, label.length() - 2);
	}

	public void setSelectedBan(Ban ban) {
		// Nếu bàn được chọn đã có đơn hàng, chọn tất cả các bàn
		// khác có cùng đơn hàng.
		DonHang donHang = ban2DonHang.get(ban);
		selectedBans.clear();
		if (donHang != null) {
			selectedBans.addAll(donHang.getBans());
			banHang.setDonHang(donHang);
		} else {
			selectedBans.add(ban);
			banHang.setDonHang(null);
		}
	}

	/**
	 * Check trạng thái có thể mở bàn hay ko. Có thể mở bàn nếu có
	 * bàn được chọn và bàn đó chưa được mở (chưa có hóa đơn).
	 * 
	 * @return trạng thái có thể mở bàn hay ko.
	 */
	public boolean getCoTheMoBan() {
		return !selectedBans.isEmpty() && (getDonHang(selectedBans.iterator().next()) == null);
	}

	/**
	 * Check trạng thái có thể chuyển bàn hay ko. Có thể chuyển bàn
	 * nếu có bàn được chọn và bàn có phải có hóa đơn (đã được mở)
	 * 
	 * @return
	 */
	public boolean getCoTheChuyenBan() {
		return !selectedBans.isEmpty() && (getDonHang(selectedBans.iterator().next()) != null);
	}

	public String thanhToan() {
		DonHang donHang = banHang.getDonHang();

		// Call service update database
		financeService.thanhToanDonHang(donHang.getId());

		// Clear the maps
		donHang2Bans.remove(donHang);
		for (Ban b : donHang.getBans()) {
			ban2DonHang.remove(b);
		}

		banHang.setDonHang(null);

		selectedBans.clear();

		// TODO //////////////// in hóa đơn /////////////////

		return "/pages/config/danhSachBanKhuVuc";
	}

	public void reset() {
		RequestContext.getCurrentInstance().reset("thanhToanFormId:thanhToanPanelGridId");
	}

	public void validateTienKhachDua(FacesContext context, UIComponent componentToValidate, Object value)
			throws ValidatorException {
		String string = SunriseStringUtils.removeNumberSeparator((String) value);
		Long tienThanhToan = 0L;
		try {
			tienThanhToan = Long.valueOf(string);
		} catch (NumberFormatException e) {
			FacesMessage message = new FacesMessage("Số tiền nhập chưa đúng. Vui lòng kiểm tra lại");
			throw new ValidatorException(message);
		}
		if (tienThanhToan < banHang.getDonHang().getTongGiaThanhToan()) {
			FacesMessage message = new FacesMessage(
					"Số tiền khách đưa không được nhỏ hơn số tiền thanh toán. Vui lòng kiểm tra lại");
			throw new ValidatorException(message);
		}
	}

	public IConfigService getConfigService() {
		return configService;
	}

	public void setConfigService(IConfigService configService) {
		this.configService = configService;
	}

	public List<KhuVucDTO> getKvDTOs() {
		return kvDTOs;
	}

	public void setKvDTOs(List<KhuVucDTO> kvDTOs) {
		this.kvDTOs = kvDTOs;
	}

	public IOrderService getOrderService() {
		return orderService;
	}

	public void setOrderService(IOrderService orderService) {
		this.orderService = orderService;
	}

	public BanHang getBanHang() {
		return banHang;
	}

	public void setBanHang(BanHang banHang) {
		this.banHang = banHang;
	}

	public IFinanceService getFinanceService() {
		return financeService;
	}

	public void setFinanceService(IFinanceService financeService) {
		this.financeService = financeService;
	}

	public Set<Ban> getSelectedBans() {
		return selectedBans;
	}

}